import os
import re
import subprocess
import shutil
import logging
import logging.config


logging.config.fileConfig(os.path.join(os.getcwd(), 'install_log.txt'))
logInstall = logging.getLogger('install')
logBuild = logging.getLogger('build')
logConfig = logging.getLogger('config')
logMain = logging.getLogger('main')

NN_SITE = """server {
    # Change these settings to match your machine
    listen 80 default_server;
    server_name 172.18.100.133;

    # Everything below here doesn't need to be changed
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    root /var/www/newznab/www/;
    index index.html index.htm index.php;

    location ~* \.(?:ico|css|js|gif|inc|txt|gz|xml|png|jpe?g) {
            expires max;
            add_header Pragma public;
            add_header Cache-Control "public, must-revalidate, proxy-revalidate";
    }

    location / { try_files $uri $uri/ @rewrites; }

    location @rewrites {
            rewrite ^/([^/\.]+)/([^/]+)/([^/]+)/? /index.php?page=$1&id=$2&subpage=$3 last;
            rewrite ^/([^/\.]+)/([^/]+)/?$ /index.php?page=$1&id=$2 last;
            rewrite ^/([^/\.]+)/?$ /index.php?page=$1 last;
    }

    location /admin { }
    location /install { }

    location ~ \.php$ {
            include /etc/nginx/fastcgi_params;
            fastcgi_pass 127.0.0.1:9000;

            # The next two lines should go in your fastcgi_params
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}"""


def loadFile(fname):
    with open(fname, 'r') as f:
        contents = f.readlines()
    return contents


def outFile(fname, contents):
    with open(fname, 'w') as f:
        f.writelines(contents)
    return


def doReplace(content, findString, repString):
    logConfig.debug('Doing replace: {} --> {}'.format(findString, repString))
    for index, line in enumerate(content):
        if re.match(findString, line):
            content[index] = repString
            return content
    logConfig.debug('Replace complete.')


def installPackages(packages):
    cmd = ['apt-get', 'install', '-y']
    cmd.extend(packages)
    logInstall.info('Installing some packages.')
    try:
        subprocess.check_call(cmd)
    except subprocess.CalledProcessError as e:
        logInstall.error('Installation failed: {}\nError: {}'.format(packages, e))
    else:
        for package in packages:
            logInstall.debug('Installed: {}'.format(package))


def addRepository(repo):
    cmd = ['add-apt-repository']
    cmd.append('ppa:' + repo)
    logInstall.info('Adding repository: {}'.format(repo))
    try:
        subprocess.check_call(cmd)
    except subprocess.CalledProcessError as e:
        logInstall.error('Could not add repository: {}\nError: {}'.format(repo, e))
    except Exception as e:
        logInstall.error('Could not add repository: {}\nError: {}'.format(repo, e))
    else:
        logInstall.info('Repo added: {}'.format(repo))


def addKeyGPG(server, keys, export):
    logInstall.info('Adding key: {}'.format(server))
    get = ['gpg', '--keyserver', server, '--recv-keys', keys]
    export = ['gpg', '-a', '--export', export]
    add = ['apt-key', 'add', '-']
    try:
        subprocess.check_call(get)
        sendToAdd = subprocess.Popen(export, stdout = subprocess.PIPE)
        addKey = subprocess.Popen(add, stdin = sendToAdd.stdout)
        sendToAdd.stdout.close()
        addKey.communicate()[0]
    except subprocess.CalledProcessError as e:
        logInstall.error('Could not install key: {}\nError: {}'.format(server, e))
    else:
        logInstall.info('Key added: {}'.format(server))


def addKeyApt(url):
    logInstall.info('Adding key: {}'.format(url))
    keyFile = (url.split('/')[-1] != '' and url.split('/')[-1])\
               or url.split('/')[-2]
    try:
        subprocess.check_call(['wget', url])
        add = subprocess.Popen('apt-key add {}'.format(keyFile),
                                stdin=subprocess.PIPE, shell=True)
        add.communicate('\n')
    except subprocess.CalledProcessError as e:
        logInstall.error('Could not install key: {}\nError: {}'.format(url, e))
    else:
        logInstall.info('Key added: {}'.format(url))


def doBuild(args, name):
    logBuild.info('Building {}.'.format(name))
    for arg in args:
        logBuild.debug('CMD: {}'.format(arg))
        if arg[0] == 'moveto':
            os.chdir(arg[1])
            continue
        try:
            subprocess.check_call(arg)
        except subprocess.CalledProcessError as e:
            logBuild.error('Installation failed: {}\nError: {}'.format(arg, e))
            return
        except Exception as e:
            logBuild.error('Another error occurred: {}'.format(e))
            return
    logBuild.info('Build successful: {}'.format(name))


def makeDirs(dirs):
    logConfig.info('Creating directories.')
    for d in dirs:
        logConfig.info('Creating {}'.format(d))
        try:
            os.makedirs(d)
        except:
            logConfig.error('Could not create directory.')
        else:
            logConfig.info('Success.')


def setPerms(perms):
    logConfig.info('Setting permissions.')
    for perm in perms:
        logConfig.debug('Permission: {}'.format(perm))
        try:
            os.chmod(perm[0], perm[1])
        except Exception as e:
            logConfig.error('Could not set permissions: {}\nError: {}'.format(perm, e))
    logConfig.info('Done.')


def installPrereqs():
    logInstall.info('Installing prerequisite packages.')
    installPackages(['ssh', 'gcc', 'make', 'build-essential', 'checkinstall', 'sysstat',
                     'python-software-properties', 'screen', 'htop', 'iptraf', 'wget',
                     'python-mysqldb', 'subversion', 'unzip', 'traceroute', 'git'])
    logInstall.info('Install complete.  Errors appear above.')


def installPHP():
    logInstall.info('Installing PHP.')
    installPackages(['php5', 'php5-dev', 'php-pear', 'php5-gd', 'php5-mysql', 'php5-curl'])
    logInstall.info('PHP install done, check errors above.')
    logConfig.info('Loading /etc/php5/cli/php.ini and editing file.')
    php = loadFile('/etc/php5/cli/php.ini')
    doReplace(php, '^;?register_globals.+$', 'register_globals = Off')
    doReplace(php, '^;?max_execution_time.+$', 'max_execution_time = 120')
    doReplace(php, '^;?memory_limit.+$', 'memory_limit = -1')
    doReplace(php, '^;?date\.timezone.+$', 'date.timezone = America/New_York')
    logConfig.info('PHP config done.  Writing file.')
    outFile('/etc/php5/cli/php.ini', php)
    logInstall.info('PHP complete.')


def installPercona():
    logInstall.info('Installing Percona.')
    addKeyGPG('hkp://keys.gnupg.net', '1C4CBDCDCD2EFD2A', 'CD2EFD2A')
    sources = ['#Percona\n', 'deb http://repo.percona.com/apt precise main\n',
               'deb-src http://repo.percona.com/apt precise main\n']
    logConfig.info('Writing apt sources list.')
    outFile('/etc/apt/sources.list.d/percona.list', sources)
    logInstall.info('Updating aptitude to install.')
    subprocess.check_call(['aptitude', 'update'])
    logInstall.info('Installing Percona packages.')
    installPackages(['percona-server-client-5.5', 'percona-server-server-5.5',
                     'libmysqlclient-dev'])
    logInstall.info('Percona complete.')


def installNginx():
    logInstall.info('Removing apache2 files and installing NGINX.')
    subprocess.check_call(['apt-get', '--purge', 'remove', '-y', 'apache2*'])
    addRepository('nginx/development')
    installPackages(['nginx', 'php5-fpm'])
    logConfig.info('Setting PHP FPM config.')
    php = loadFile('/etc/php5/fpm/php.ini')
    doReplace(php, '^;?register_globals.+$', 'register_globals = Off')
    doReplace(php, '^;?max_execution_time.+$', 'max_execution_time = 120')
    doReplace(php, '^;?memory_limit.+$', 'memory_limit = -1')
    doReplace(php, '^;?date\.timezone.+$', 'date.timezone = America/New_York')
    logConfig.info('Writing out new FPM config.')
    outFile('/etc/php5/fpm/php.ini', php)
    logConfig.info('Restarting php5-fpm.')
    subprocess.check_call(['/etc/init.d/php5-fpm', 'restart'])
    logConfig.info('Writing out newznab server directives to NGINX.')
    with open('/etc/nginx/sites-available/newznab', 'w') as f:
        f.write(NN_SITE)
    makeDirs(['/var/log/nginx'])
    setPerms([ ['/var/log/nginx', 0777] ])
    logConfig.info('Removing default site.')
    os.unlink('/etc/nginx/sites-enabled/default')
    os.link('/etc/nginx/sites-available/newznab', '/etc/nginx/sites-enabled/newznab')
    logConfig.info('Restarting NGINX.')
    subprocess.check_call(['service', 'nginx', 'restart'])
    logInstall.info('NGINX complete.')


def installExtras():
    packages = ['unrar', 'libfaac-dev', 'libjack-jackd2-dev', 'libopencore-amrwb-dev',
                'libmp3lame-dev', 'libopencore-amrnb-dev',
                'libsdl1.2-dev', 'libtheora-dev', 'libva-dev', 'libvdpau-dev',
                'libvorbis-dev', 'libx11-dev', 'libxfixes-dev', 'texi2html', 'zlib1g-dev',
                'libdirac-dev', 'libxvidcore-dev', 'lame']

    yasm = [ ['wget', 'http://www.tortall.net/projects/yasm/releases/yasm-1.2.0.tar.gz'],
             ['tar', '-xvf', 'yasm-1.2.0.tar.gz'],
             ['moveto', 'yasm-1.2.0'],
             ['./configure'],
             ['make'],
             ['make', 'install'],
             ['moveto', '../' ] ]

    x264 = [ ['wget', 'ftp://ftp.videolan.org/pub/videolan/x264/snapshots/last_stable_x264.tar.bz2'],
             ['mkdir', 'x264'],
             ['tar', '--strip-components=1', '-jxf', 'last_stable_x264.tar.bz2', '-C', 'x264'],
             ['moveto', 'x264'],
             ['./configure', '--enable-static'],
             ['make'],
             ['checkinstall', '--pkgname=x264',
              '--pkgversion="3:$(./version.sh | awk -F\'[" ]\' \'/POINT/{print $4$5}\')"',
              '--backup=no', '--deldoc=yes', '--fstrans=no', '--default'],
             ['moveto', '../'] ]

    ffmpeg = [ ['wget', 'http://ffmpeg.org/releases/ffmpeg-0.8.7.tar.gz'],
               ['tar', 'xvfz', 'ffmpeg-0.8.7.tar.gz'],
               ['moveto', 'ffmpeg-0.8.7'],
               ['./configure', '--enable-gpl', '--enable-libfaac', '--enable-libmp3lame',
                '--enable-libopencore-amrnb', '--enable-libopencore-amrwb', '--enable-libtheora',
                '--enable-libvorbis', '--enable-libx264', '--enable-nonfree', '--enable-postproc',
                '--enable-version3', '--enable-x11grab', '--enable-libdirac', '--enable-libxvid'],
               ['make'],
               ['checkinstall', '--pkgname=ffmpeg', '--pkgversion="5:0.8.7', '--backup=no',
                '--deldoc=yes', '--fstrans=no', '--default'],
               #['hash', 'x264', 'ffmpeg', 'ffplay', 'ffprobe'],
               ['moveto', '../'] ]

    sphinx = [ ['wget', 'http://sphinxsearch.com/files/sphinx-2.1.1-beta.tar.gz'],
               ['tar', 'xvzf', 'sphinx-2.1.1-beta.tar.gz'],
               ['moveto', 'sphinx-2.1.1-beta'],
               ['wget', 'http://snowball.tartarus.org/dist/libstemmer_c.tgz'],
               ['tar', '--strip-components=1', '-zxf', 'libstemmer_c.tgz', '-C', 'libstemmer_c'],
               ['./configure', '--prefix=/usr/local', '--with-libstemmer'],
               ['make', '-j2'],
               ['checkinstall', '--pkgname=sphinx', '--pkgversion="2.1.1-beta"', '--backup=no',
                '--deldoc=yes', '--fstrans=no', '--default'] ]

    installPackages(packages)
    doBuild(yasm, 'yasm')
    doBuild(x264, 'x264')
    doBuild(ffmpeg, 'ffmpeg')
    doBuild(sphinx, 'sphinx')
    addRepository('shiki/mediainfo')
    subprocess.check_call(['aptitude', 'update'])
    installPackages(['mediainfo'])


def installNewznab():
    logInstall.info('Downloading newznab source.')
    subprocess.call(['svn', 'co', 'svn://svn.newznab.com/nn/branches/nnplus', '/var/www/newznab/'])
    makeDirs(['/var/www/newznab/nzbfiles/tmpunrar'])
    setPerms([ ['/var/www/newznab/www/lib/smarty/templates_c', 0777],
               ['/var/www/newznab/www/covers/movies', 0777],
               ['/var/www/newznab/www/covers/anime', 0777],
               ['/var/www/newznab/www/covers/music', 0777],
               ['/var/www/newznab/www', 0777],
               ['/var/www/newznab/www/install', 0777],
               ['/var/www/newznab/nzbfiles', 0777],
               ['/var/www/newznab/nzbfiles/tmpunrar', 0777] ])
    logInstall.info('Newznab install complete.')


def installSQLBuddy():
    logInstall.info('Downloading SQLBuddy.')
    try:
        subprocess.call(['wget', 'https://github.com/calvinlough/sqlbuddy/zipball/master'])
        subprocess.call(['unzip', 'master'])
    except:
        logInstall.error('SQLBuddy not installed.')
        return
    buddy = [e for e in os.listdir(os.getcwd()) if 'sqlbuddy' in e][0]
    os.rename(buddy, 'sqlbuddy')
    try:
        shutil.move('sqlbuddy', '/var/www/newznab/www/')
    except Exception as e:
        logInstall.error('SQLBuddy folder could not be moved: {}'.format(e))
    else:
        logInstall.info('SQLBuddy installed.')


def installWebmin():
    logInstall.info('Installing Webmin.')
    addKeyApt('http://www.webmin.com/jcameron-key.asc')
    sources = ['#Webmin\n', 'deb http://download.webmin.com/download/repository sarge contrib\n',
               'deb http://webmin.mirror.somersettechsolutions.co.uk/repository sarge contrib']
    outFile('/etc/apt/sources.list.d/webmin.list', sources)
    subprocess.call(['aptitude', 'update'])
    installPackages(['webmin'])
    logInstall.info('Done with Webmin.')


def makeLogDirs():
    logConfig.info('Creating log directory.')
    makeDirs(['/var/log/nntools'])
    setPerms([ ['/var/log/nntools', 0777] ])

def doCleanup():
    logMain.info('Doing some cleanup.')
    subprocess.call(['apt-get', '-y', 'autoclean'])
    subprocess.call(['apt-get', '-y', 'autoremove'])
    logMain.info('Done.')


def main():
    installPrereqs()
    makeDirs(['/var/www/newznab'])
    setPerms([ ['/var/www/newznab', 0777] ])
    installPHP()
    installPercona()
    installNginx()
    installExtras()
    installNewznab()
    installSQLBuddy()
    installWebmin()
    makeLogDirs()
    doCleanup()

if __name__ == '__main__':
    main()
